package com.springsource.autowired;

import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Configuration
public class A47_2 {

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A47_2.class);
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
//        testArray(beanFactory);
//        testList(beanFactory);
//        testApplicationContext(beanFactory);
//        testGeneric(beanFactory);
        testQualifier(beanFactory);
    }

    private static void testQualifier(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
        DependencyDescriptor dd = new DependencyDescriptor(Target.class.getDeclaredField("service"), false);
        Class<?> dependencyType = dd.getDependencyType();
        String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, dependencyType);
        ContextAnnotationAutowireCandidateResolver contextAnnotationAutowireCandidateResolver = new ContextAnnotationAutowireCandidateResolver();
        contextAnnotationAutowireCandidateResolver.setBeanFactory(beanFactory);
        for (String beanName : beanNames) {
            BeanDefinition mergedBeanDefinition = beanFactory.getMergedBeanDefinition(beanName);
            // 对beanName进行对比
            if (contextAnnotationAutowireCandidateResolver.isAutowireCandidate(new BeanDefinitionHolder(mergedBeanDefinition, beanName), dd)) {
                System.out.println(dd.resolveCandidate(beanName, dependencyType, beanFactory));
            }
        }
    }

    private static void testGeneric(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
        DependencyDescriptor dd = new DependencyDescriptor(Target.class.getDeclaredField("dao"), false);
        Class<?> dependencyType = dd.getDependencyType();
        String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, dependencyType);
        ContextAnnotationAutowireCandidateResolver contextAnnotationAutowireCandidateResolver = new ContextAnnotationAutowireCandidateResolver();
        contextAnnotationAutowireCandidateResolver.setBeanFactory(beanFactory);
        for (String beanName : beanNames) {
            BeanDefinition mergedBeanDefinition = beanFactory.getMergedBeanDefinition(beanName);
            // 对比泛型信息，也是对比Qualifier中的bean的名字
            if (contextAnnotationAutowireCandidateResolver.isAutowireCandidate(new BeanDefinitionHolder(mergedBeanDefinition, beanName), dd)) {
                System.out.println(dd.resolveCandidate(beanName, dependencyType, beanFactory));
            }
        }

    }

    private static void testApplicationContext(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException, IllegalAccessException {
        // 这种特殊的容器对象不是和bean一样都放在DefaultSingletonBeanRegistry.singletonObjects中
        // 而是放在DefaultListableBeanFactory.resolvableDependencies中，在refresh-prepareBeanFactory方法中加入
        DependencyDescriptor dd = new DependencyDescriptor(Target.class.getDeclaredField("applicationContext"), false);
        Class<?> dependencyType = dd.getDependencyType();
        Field resolvableDependencies = DefaultListableBeanFactory.class.getDeclaredField("resolvableDependencies");
        resolvableDependencies.setAccessible(true);
        Map<Class<?>, Object> dependencies = (Map<Class<?>, Object>) resolvableDependencies.get(beanFactory);
        for (Map.Entry<Class<?>, Object> entry : dependencies.entrySet()) {
            Class<?> key = entry.getKey();
            System.out.println(key);
            System.out.println(dependencyType);
            if (key.isAssignableFrom(dependencyType)) {
                System.out.println(entry.getValue());
                break;
            }
        }

    }

    private static void testList(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
        DependencyDescriptor dd = new DependencyDescriptor(Target.class.getDeclaredField("serviceList"), false);
        Class<?> dependencyType = dd.getDependencyType();
        if (dependencyType == List.class) {
            List<Object> beans = new ArrayList<>();
            dd.increaseNestingLevel();
            Class<?> deeperDependencyType = dd.getDependencyType();
            String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, deeperDependencyType);
            for (String beanName : beanNames) {
                Object o = dd.resolveCandidate(beanName, deeperDependencyType, beanFactory);
                beans.add(o);
            }
            System.out.println(beans);
        }
        // 如果不做increaseNestingLevel，可以获取泛型，Spring正常应该是这么处理的
//        dd.getResolvableType().getGeneric(0).resolve();
    }

    private static void testArray(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
        DependencyDescriptor dd = new DependencyDescriptor(Target.class.getDeclaredField("serviceArray"), false);
        Class<?> dependencyType = dd.getDependencyType();
        if (dependencyType.isArray()) {
            List<Object> beans = new ArrayList<>();
            Class<?> componentType = dependencyType.getComponentType();
            String[] strings = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, componentType);
            for (String beanName : strings) {
                Object o = dd.resolveCandidate(beanName, componentType, beanFactory);
                beans.add(o);
            }
            Object o = beanFactory.getTypeConverter().convertIfNecessary(beans, dependencyType);
            System.out.println(o);
        }
    }

    static class Target {
        @Autowired
        private Service[] serviceArray;
        @Autowired
        private List<Service> serviceList;
        @Autowired
        private ConfigurableApplicationContext applicationContext;
        @Autowired
        private Dao<Teacher> dao;
        @Autowired
        @Qualifier("service2")
        private Service service;
    }

    interface Service {}

    @Component("service1")
    static class Service1 implements Service {}

    @Component("service2")
    static class Service2 implements Service {}

    @Component("service3")
    static class Service3 implements Service {}

    interface Dao<T> {}

    static class Student{}

    static class Teacher {}

    @Component("dao1")
    static class Dao1 implements Dao<Student> {}
    @Component("dao2")
    static class Dao2 implements Dao<Teacher> {}




}
